import Modal from "components/common/Modal";
import { Icon } from "components/common/Icon";
import React, { useState } from "react";
import Form from "react-bootstrap/Form";
import Button from "react-bootstrap/Button";
import { useAsync, UseAsyncReturn } from "react-async-hook";
import { useServices } from "hooks/useServices";
import { useAppSelector } from "components/store";
import { databaseSelectors } from "components/common/shell/databaseSliceSelectors";
import { LazyLoad } from "components/common/LazyLoad";
import collectionsStats from "models/database/documents/collectionsStats";
import virtualGridController from "widgets/virtualGrid/virtualGridController";
import document from "models/database/documents/document";
import virtualGridSelection from "widgets/virtualGrid/virtualGridSelection";
import messagePublisher from "common/messagePublisher";
import collection from "models/database/documents/collection";
import ButtonWithSpinner from "components/common/ButtonWithSpinner";
import notificationCenter from "common/notifications/notificationCenter";
import collectionsTracker from "common/helpers/database/collectionsTracker";
import pluralizeHelpers from "common/helpers/text/pluralizeHelpers";
import genUtils from "common/generalUtils";
import studioSettings from "common/settings/studioSettings";
import { Switch } from "components/common/Checkbox";
import useBoolean from "hooks/useBoolean";
import { useIsMounted } from "components/hooks/useIsMounted";

interface DeleteDocumentsModalProps {
    close: () => void;
    gridController: virtualGridController<document>;
    onDeleteCompleted?: () => void;
    currentCollection: KnockoutObservable<collection>;
}

export default function DeleteDocumentsModal({
    close,
    gridController,
    onDeleteCompleted,
    currentCollection,
}: DeleteDocumentsModalProps) {
    const selection = gridController.selection();

    // Note: wrapped in function to avoid type error (JQueryPromise<globalSettings>)
    const asyncGlobalSettings = useAsync(async () => await studioSettings.default.globalSettings(), []);

    const isRequireTypedConfirm =
        asyncGlobalSettings.result?.isRequireTypedConfirmationToDeleteDocuments.getValue() ?? true;

    const isSelectedAll = selection.count === currentCollection().documentCount();

    const dbName = useAppSelector(databaseSelectors.activeDatabaseName);

    const { confirmText, handleTextChange, isConfirmed } = useDeleteConfirmation(isRequireTypedConfirm);
    const { tasksService } = useServices();
    const collectionsList = useAsync(() => tasksService.fetchCollectionsStats(dbName), []);

    const deleteCollection = useDeleteCollection(
        currentCollection,
        selection.excluded.map((doc) => doc.getId()),
        selection.count,
        close,
        onDeleteCompleted
    );

    const onConfirm = () => {
        if (!isConfirmed) {
            return;
        }
        deleteCollection.execute();
    };

    const toggleIsRequireTypedConfirm = async () => {
        if (!asyncGlobalSettings.result) {
            messagePublisher.reportError("Failed to load studio global settings");
            return;
        }

        asyncGlobalSettings.result.isRequireTypedConfirmationToDeleteDocuments.setValue(!isRequireTypedConfirm);
        await asyncGlobalSettings.execute();
    };

    return (
        <Modal show contentClassName="modal-border bulge-danger">
            <Modal.Header closeButton className="vstack gap-4" onCloseClick={close}>
                <div className="text-center">
                    <Icon icon="trash" color="danger" className="fs-1" margin="m-0" />
                </div>
                <div className="text-center lead">Delete {isSelectedAll ? "all" : "selected"} documents?</div>
            </Modal.Header>
            <Modal.Body>
                <CollectionsInfo
                    collectionsList={collectionsList}
                    virtualGridSelection={selection}
                    currentCollection={currentCollection}
                    isSelectedAll={isSelectedAll}
                />
                {isRequireTypedConfirm && (
                    <Form.Group>
                        <Form.Label className="fw-bold">Type DELETE to confirm</Form.Label>
                        <Form.Control placeholder="DELETE" value={confirmText} onChange={handleTextChange} />
                    </Form.Group>
                )}
            </Modal.Body>
            <Modal.Footer className="hstack justify-content-between">
                <Switch selected={isRequireTypedConfirm} toggleSelection={toggleIsRequireTypedConfirm} color="primary">
                    Require typed confirmation
                </Switch>
                <div className="hstack gap-2 flex-grow-1 justify-content-end">
                    <Button variant="link" onClick={close} className="link-muted">
                        Cancel
                    </Button>
                    <ButtonWithSpinner
                        isSpinning={deleteCollection.loading}
                        variant="danger"
                        onClick={onConfirm}
                        className="rounded-pill"
                        disabled={!isConfirmed || collectionsList.loading}
                    >
                        Delete
                    </ButtonWithSpinner>
                </div>
            </Modal.Footer>
        </Modal>
    );
}

function useDeleteConfirmation(isRequireTypedConfirm: boolean) {
    const [confirmText, setConfirmText] = useState("");

    const handleTextChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        setConfirmText(e.target.value.trim());
    };

    return {
        confirmText,
        handleTextChange,
        isConfirmed: isRequireTypedConfirm ? confirmText === "DELETE" : true,
    };
}

function useDeleteCollection(
    currentCollection: KnockoutObservable<collection>,
    excludedIds: string[],
    documentCount: number,
    close: () => void,
    onDeleteCompleted: () => void
) {
    const { databasesService } = useServices();
    const dbName = useAppSelector(databaseSelectors.activeDatabaseName);
    const { value: isLoading, setValue: setIsLoading } = useBoolean(false);
    const isMounted = useIsMounted();

    const execute = () => {
        setIsLoading(true);
        const collectionNameForApi = currentCollection().isAllDocuments ? "@all_docs" : currentCollection().name;

        // This is JQueryPromise. Use 'done' to prevent wrong order of execution in event loop
        databasesService
            .deleteCollection(collectionNameForApi, dbName, excludedIds)
            .done((result) => {
                const operationId = result.OperationId;

                // For slow connection, this operation may fail
                try {
                    notificationCenter.instance.openDetailsForOperationById(dbName, operationId);
                } catch {
                    onDeleteCompleted();
                    close();
                    return;
                }

                notificationCenter.instance.databaseOperationsWatch
                    .monitorOperation(operationId)
                    .done(() => {
                        if (excludedIds.length === 0) {
                            messagePublisher.reportSuccess(`Deleted collection ${currentCollection().name}`);
                        } else {
                            messagePublisher.reportSuccess(
                                `Deleted ${pluralizeHelpers.pluralize(documentCount, "document", "documents")} from ${currentCollection().name}`
                            );
                        }

                        if (excludedIds.length === 0) {
                            // if entire collection was deleted then go to 'all documents'
                            const allDocsCollection = collectionsTracker.default.getAllDocumentsCollection();
                            if (currentCollection() !== allDocsCollection) {
                                currentCollection(allDocsCollection);
                            }
                        }
                    })
                    .always(() => {
                        onDeleteCompleted();
                        close();
                    });
            })
            .always(() => {
                if (isMounted()) {
                    setIsLoading(false);
                }
            });
    };

    return {
        execute,
        loading: isLoading,
    };
}

interface CollectionsInfoProps {
    virtualGridSelection: virtualGridSelection<document>;
    collectionsList: UseAsyncReturn<collectionsStats>;
    currentCollection: KnockoutObservable<collection>;
    isSelectedAll: boolean;
}

function CollectionsInfo({
    virtualGridSelection,
    collectionsList,
    currentCollection,
    isSelectedAll,
}: CollectionsInfoProps) {
    const collectionName =
        currentCollection().name === collection.allDocumentsCollectionName ? "all" : currentCollection().name;

    const isAllDocuments = collectionName === "all";

    return (
        <LazyLoad active={collectionsList.loading}>
            <p>
                {isSelectedAll ? "All" : "Selected"} documents from{" "}
                {isAllDocuments ? (
                    <>
                        <b className="text-uppercase">{collectionName}</b> collections
                    </>
                ) : (
                    <>
                        collection <b>{collectionName}</b>
                    </>
                )}{" "}
                will be deleted ({genUtils.formatNumberToStringFixed(virtualGridSelection.count, 0)} documents).
            </p>
        </LazyLoad>
    );
}
